/*
    日志器模块：
        1.抽象出一个Logger父类，派生出同步/异步日志器子类
		2.抽象出落地操作，派生出不同的落地方式子类，让不同的日志器调用各自的方式
*/
#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__


#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>
#include "looper.hpp"
#include <unordered_map>

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif 

namespace log{
    class Logger{
    public:
        using ptr = shared_ptr<Logger>;

        Logger(const string &logger_name, LogLevel::value limit_level, Formatter::ptr &pformatter, vector<LogSink::ptr> &sinks)
        :_logger_name(logger_name), _limit_level(limit_level), _pformatter(pformatter), _sinks(sinks.begin(), sinks.end()){
            
        }

        const string& name(){
            return _logger_name;
        }

        //构造日志消息对象并格式化，得到格式化后的日志消息字符串，再用不同落地接口进行落地输出
        void debug(const string &file, size_t line, const string &fmt, ...){
            //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            //1、判断当前日志是否达到输出等级
            if(_limit_level > LogLevel::value::DEBUG){
                //限制等级大于函数等级则不能输出
                return;
            }

            //2、对fmt和不定参进行组织，得到日志消息字符串
            va_list ap;
            va_start(ap, fmt);
            char *buf;
            //从ap开始，按fmt格式存入buf中
            int ret = vasprintf(&buf, fmt.c_str(), ap);
            if(ret == -1){
                cout << "vasprintf() err!" << endl;
                return;
            }
            va_end(ap);
            //3、构造LogMsg对象
            //4、通过格式化工具对LogMsg进行格式化，得到格式化后的字符串
            //5、进行日志落地
            lmsgSerializeAndLoad(LogLevel::value::DEBUG, file, line, buf);
            free(buf);
        }

        void info(const string &file, size_t line, const string &fmt, ...){
                        //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            //1、判断当前日志是否达到输出等级
            if(_limit_level > LogLevel::value::INFO){
                //限制等级大于函数等级则不能输出
                return;
            }

            //2、对fmt和不定参进行组织，得到日志消息字符串
            va_list ap;
            va_start(ap, fmt);
            char *buf;
            //从ap开始，按fmt格式存入buf中
            int ret = vasprintf(&buf, fmt.c_str(), ap);
            if(ret == -1){
                cout << "vasprintf() err!" << endl;
                return;
            }
            va_end(ap);
            //3、构造LogMsg对象
            //4、通过格式化工具对LogMsg进行格式化，得到格式化后的字符串
            //5、进行日志落地
            lmsgSerializeAndLoad(LogLevel::value::INFO, file, line, buf);
            free(buf);
        }

        void warning(const string &file, size_t line, const string &fmt, ...){
                        //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            //1、判断当前日志是否达到输出等级
            if(_limit_level > LogLevel::value::WARNING){
                //限制等级大于函数等级则不能输出
                return;
            }

            //2、对fmt和不定参进行组织，得到日志消息字符串
            va_list ap;
            va_start(ap, fmt);
            char *buf;
            //从ap开始，按fmt格式存入buf中
            int ret = vasprintf(&buf, fmt.c_str(), ap);
            if(ret == -1){
                cout << "vasprintf() err!" << endl;
                return;
            }
            va_end(ap);
            //3、构造LogMsg对象
            //4、通过格式化工具对LogMsg进行格式化，得到格式化后的字符串
            //5、进行日志落地
            lmsgSerializeAndLoad(LogLevel::value::WARNING, file, line, buf);
            free(buf);
        }

        void error(const string &file, size_t line, const string &fmt, ...){
                        //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            //1、判断当前日志是否达到输出等级
            if(_limit_level > LogLevel::value::ERROR){
                //限制等级大于函数等级则不能输出
                return;
            }

            //2、对fmt和不定参进行组织，得到日志消息字符串
            va_list ap;
            va_start(ap, fmt);
            char *buf;
            //从ap开始，按fmt格式存入buf中
            int ret = vasprintf(&buf, fmt.c_str(), ap);
            if(ret == -1){
                cout << "vasprintf() err!" << endl;
                return;
            }
            va_end(ap);
            //3、构造LogMsg对象
            //4、通过格式化工具对LogMsg进行格式化，得到格式化后的字符串
            //5、进行日志落地
            lmsgSerializeAndLoad(LogLevel::value::ERROR, file, line, buf);
            free(buf);
        }

        void fatal(const string &file, size_t line, const string &fmt, ...){
                        //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            //1、判断当前日志是否达到输出等级
            if(_limit_level > LogLevel::value::FATAL){
                //限制等级大于函数等级则不能输出
                return;
            }

            //2、对fmt和不定参进行组织，得到日志消息字符串
            va_list ap;
            va_start(ap, fmt);
            char *buf;
            //从ap开始，按fmt格式存入buf中
            int ret = vasprintf(&buf, fmt.c_str(), ap);
            if(ret == -1){
                cout << "vasprintf() err!" << endl;
                return;
            }
            va_end(ap);
            //3、构造LogMsg对象
            //4、通过格式化工具对LogMsg进行格式化，得到格式化后的字符串
            //5、进行日志落地
            lmsgSerializeAndLoad(LogLevel::value::FATAL, file, line, buf);
            free(buf);
        }

    protected:
        //抽象落地接口
        virtual void loadLog(const char *data, size_t len) = 0;

        //对日志消息进行序列化，包含：
        //  3、构造LogMsg对象
        //  4、通过格式化工具对LogMsg进行格式化，得到格式化后的字符串
        //  5、进行日志落地
        void lmsgSerializeAndLoad(LogLevel::value limit_level, const string &file, size_t line, char* buf){
            //3、构造LogMsg对象
            LogMsg lmsg(limit_level, file, line, _logger_name, buf);

            //4、通过格式化工具对LogMsg进行格式化，得到格式化后的字符串
            // stringstream ss;
            // _formatter->toFormat(ss, lmsg);
            string str = _pformatter->toFormat(lmsg);

            //5、进行日志落地
            loadLog(str.c_str(), str.size());
        }

    protected:
        string _logger_name;        
        atomic<LogLevel::value> _limit_level;       //日志输出限制等级，用atomic封装，保证原子性（加锁的话锁太多会降低效率，且可能会导致锁冲突）
        Formatter::ptr _pformatter;      //格式化模块对象的指针
        vector<LogSink::ptr> _sinks;    //落地模块对象数组
        mutex _mtx;     //互斥锁
    };


    //同步日志器
    class SyncLogger : public Logger{
    public:
        //用抽象父类进行构造
        SyncLogger(const string &logger_name, LogLevel::value limit_level, Formatter::ptr &formatter, vector<LogSink::ptr> &sinks)
        :Logger(logger_name, limit_level, formatter, sinks){
            
        }
    protected:
        //自己将日志直接通过落地模块句柄进行日志落地
        void loadLog(const char *data, size_t len) override{
            //智能指针管理锁，自动加解锁
            //防止多线程调用同一个对象时    1.遍历落地模块对象数组时_sinks被别的线程更改  2.writeLog时多个线程写入同一data
            unique_lock<mutex> lock(_mtx);

            if(_sinks.empty()){
                return;
            }
            //遍历落地模块对象数组，多种落地方式
            for(auto &sit : _sinks){
                sit->writeLog(data, len);
            }
        }
    };

    //异步日志器
    class AsyncLogger : public Logger{
    public:
        AsyncLogger(const string &logger_name, LogLevel::value limit_level, Formatter::ptr &formatter, vector<LogSink::ptr> &sinks, AsyncType looper_type)
        :Logger(logger_name, limit_level, formatter, sinks), 
         _plooper(make_shared<AsyncLooper>(bind(&AsyncLogger::realLog, this, std::placeholders::_1), looper_type)){
                                            //bind通过realLog()生成一个新的函数
                                            //由于realLog()本身就有一个隐含的this，这里只是传入this填补了这个隐含参数
                                            //所以新函数仍然只有一个参数Buffer &buf
            
        }

        //将数据写入缓冲区，通过异步工作器进行落地
        void loadLog(const char *data, size_t len) override{
            //push自己就是线程安全的
            _plooper->push(data, len);
        }

        //落地函数，将缓冲区中的数据落地
        void realLog(Buffer &buf){
            //异步日志器是单执行流的独立的线程，内部是穿行的，在落地的时候不需要加锁，只要保证写入缓冲区的时候是线程安全的就行了
            if(_sinks.empty()){
                return;
            }
            //遍历落地模块对象数组，多种落地方式
            for(auto &sit : _sinks){
                sit->writeLog(buf.getBegin(), buf.readableSize());
            }
        }
        
    private:
        AsyncLooper::ptr _plooper;
    };

    //日志器建造者：帮助用户构造日志器，简化用户使用复杂度
    //1、抽象建造者类
    //  1.1、设置日志器类型
    //  1.2、将不同类型的日志器的创建放到同一个日志器建造者类中完成
    //2、派生出具体建造者类
    //  2.1、局部
    //  2.2、全局 -- 添加到全局单例管理器中
    enum LoggerType{
        SYNC,
        ASYNC
    };

    class LoggerBuilder{
    public:
        LoggerBuilder():_logger_type(LoggerType::SYNC), _limit_level(LogLevel::value::DEBUG), _looper_type(AsyncType::ASYNC_SAFE){

        }

        void buildLoggerType(LoggerType type){
            _logger_type = type;
        }

        void setAsyncUnsafe(){
            _looper_type = AsyncType::ASYNC_UNSAFE;
        }

        void buildLoggerName(const string& name){
            _logger_name = name;
        }

        void buildLoggerLevel(LogLevel::value level){
            _limit_level = level;
        }

        void buildFormatter(const string& pattern){
            _pformatter = make_shared<Formatter>(pattern);
        }

        template<class SinkType, class ...Args>
        void buildSink(Args&& ...args){
            LogSink::ptr psink = SinkFactory::createSink<SinkType>(forward<Args>(args)...);
            _sinks.push_back(psink);
        }

        virtual Logger::ptr build() = 0;

    protected:
        LoggerType _logger_type;
        string _logger_name;
        atomic<LogLevel::value> _limit_level;       //日志输出限制等级，用atomic封装，保证原子性（加锁的话锁太多会降低效率，且可能会导致锁冲突）
        Formatter::ptr _pformatter;      //格式化模块对象的指针
        vector<LogSink::ptr> _sinks;    //落地模块对象数组
        AsyncType _looper_type;
    };


    //局部日志器建造者
    class LocalLoggerBuilder : public LoggerBuilder{
    public:
        Logger::ptr build() override{
            //必须有日志器名称
            assert(!_logger_name.empty());
            //如果没有格式化模块对象和落地模块对象则创建
            if(_pformatter.get() == nullptr){
                _pformatter = make_shared<Formatter>();
            }
            if(_sinks.empty()){
                //_sinks.push_back(make_shared<outSink>());
                buildSink<outSink>();
            }
            if(_logger_type == ASYNC){
                return make_shared<AsyncLogger>(_logger_name, _limit_level, _pformatter, _sinks, _looper_type);
            }

            return make_shared<SyncLogger>(_logger_name, _limit_level, _pformatter, _sinks);
        }
    };


    //日志器管理者（懒汉单例）
    class LoggerManager{
    public:
        //获取单例
        static LoggerManager& getInstance(){
            static LoggerManager _eton;
            return _eton;
        }

        //添加日志器
        void addLogger(Logger::ptr &logger){
            //如果日志器已存在则直接返回
            if(findLogger(logger->name())){
                return;
            }
            unique_lock<mutex> lock(_mtx);
            _loggers.insert(make_pair(logger->name(), logger));
        }

        //查找日志器是否已存在
        bool findLogger(const string &name){
            unique_lock<mutex> lock(_mtx);

            auto it = _loggers.find(name);
            if(it == _loggers.end()){
                return false;
            }else{
                return true;
            }
        }

        //获取日志器
        Logger::ptr getLogger(const string &name){
            unique_lock<mutex> lock(_mtx);

            auto it = _loggers.find(name);
            //未找到返回空指针
            if(it == _loggers.end()){
                return Logger::ptr();
            }else{
                return it->second;
            }
            

        }

        //获取默认日志器
        Logger::ptr rootLogger(){
            return _root_logger;
        }

    private:
        LoggerManager(){
            //本地的日志器建造者
            unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
            //只用设置名称，其他都会自动设置
            builder->buildLoggerName("root");
            //建造一个日志器
            _root_logger = builder->build();
            _loggers.insert(make_pair(_root_logger->name(),_root_logger));
        }

    private:
        mutex _mtx;
        Logger::ptr _root_logger;       //默认日志器
        unordered_map<string, Logger::ptr> _loggers;   //日志器哈希表，日志器名称和日志器指针进行映射

    };


    //全局日志器建造者 -- 在局部的基础上增加了一个功能：将日志器添加到单例对象中
    class GlobalLoggerBuilder : public LoggerBuilder{
    public:
        Logger::ptr build() override{
            //必须有日志器名称
            assert(!_logger_name.empty());
            //如果没有格式化模块对象和落地模块对象则创建
            if(_pformatter.get() == nullptr){
                _pformatter = make_shared<Formatter>();
            }
            if(_sinks.empty()){
                //_sinks.push_back(make_shared<outSink>());
                buildSink<outSink>();
            }

            Logger::ptr logger;
            if(_logger_type == ASYNC){
                logger = make_shared<AsyncLogger>(_logger_name, _limit_level, _pformatter, _sinks, _looper_type);
            }else{
                logger = make_shared<SyncLogger>(_logger_name, _limit_level, _pformatter, _sinks);
            }

            //调用单例管理者，将日志器交给管理者
            LoggerManager::getInstance().addLogger(logger);

            return logger;
        }
    };

}

#endif